/* locktest.c
 *	Simple program to test the lock-related syscalls
 *	
 *	Does syscalls that create, acquire, release, and destroy locks and create, wait, signal, broadcast, and destory CVs
 */
#include "syscall.h"

void CreateLockTest();
void AcquireTest();
void ReleaseTest();
void DestroyLockTest();

void ThreadA();
void ThreadB();
void Fork_Test();

int FTLock;
int FTCV;
int threadALock;

/*Thread*threadA;*/
void (*threadA)(void);

/*Thread*threadB;*/
void (*threadB)(void);


int main(int argc, char **argv)
{   
	/* Must run tests with -d t to see all error output\n");*/
	int i;
	AcquireTest();
	ReleaseTest();
    DestroyLockTest();
	
	threadALock = CreateLock(0);
	FTLock = CreateLock(0);
	FTCV = CreateCV(0);
	
	Fork_Test();

	for(i=0; i <10000; i++)
	{
		Yield(0);
	}

	CreateLockTest();
}

void CreateLockTest()
{
    int lockIndex, i;

    PrintOut("\n\n********************CREATELOCK TESTS*******************\n\n", 59); 
  
    /*Correct Use - Should Pass*/
	
	PrintOut("Test 1: Using Create Correctly.................................\n", 64); 
    CreateLock(1);
    
    PrintOut("\nTest 2: Using Create Repeatedly to create different locks.....\n", 64); 
	for (i = 0; i < 10; i++)
    {
        lockIndex = CreateLock(1); /*Create a lock. An int that indexes the locks location in an array in the kernel will be returned*/
    }
    
    PrintOut("\nTest 3: Using Create, Acquire, Release........................\n", 64); 
	lockIndex = CreateLock(1); /*Create a lock. An int that indexes the locks location in an array in the kernel will be returned*/
    Acquire(lockIndex,1); /*Acquire a lock. The int indexes the lock's location in an array in the kernel. Returns error code, 0 - SUCCESS*/
    Release(lockIndex,1); /*Release a lock. The int indexes the lock's location in an array in the kernel. Returns error code, 0 - SUCCESS*/
    
    /*Incorrect Use - Should Fail and/or print error message*/
    
	PrintOut("\nTest 4: Using Create/Release/Acquire..........................\n", 64); /*Run with -d t*/
    lockIndex = CreateLock(1); /*Create a lock. An int that indexes the locks location in an array in the kernel will be returned*/
    Release(lockIndex,1); /*Release a lock. The int indexes the lock's location in an array in the kernel. Returns error code, 0 - SUCCESS*/
    Acquire(lockIndex,1); /*Acquire a lock. The int indexes the lock's location in an array in the kernel. Returns error code, 0 - SUCCESS*/

	PrintOut("\nTest 5: Using Create to make more locks than the maximum......\n", 64); 
    for (i = 0; i < 200; i++)
    {
        CreateLock(1); /*Create a lock. An int that indexes the locks location in an array in the kernel will be returned*/
    }
}

void AcquireTest()
{
    int lockIndex, i;
    
    PrintOut("\n\n********************ACQUIRE TESTS***********************\n\n", 60); 
    
    /*Correct Use - Should Pass*/
	
    PrintOut("Test 1: Using Acquire on newly created lock....................\n", 64);
	lockIndex = CreateLock();
    Acquire(lockIndex);
	
    PrintOut("\nTest 2: Using Acquire after a Release on same lock............\n", 64);
	Release(lockIndex);
    Acquire(lockIndex);
	
    PrintOut("\nTest 3: Using Acquire while already the owner.................\n", 64);
    lockIndex = CreateLock();
    Acquire(lockIndex);
    Acquire(lockIndex);
	
    /*Incorrect Use - Should Fail and print error message*/

    PrintOut("\nTest 4: Using Acquire on uninitialized lock...................\n", 64);
	Acquire(100);

	PrintOut("\nTest 5: Using Acquire on a lock index out of bounds...........\n", 64);
    Acquire(500);   

    PrintOut("\nTest 6: Using Acquire on a negative lock index................\n", 64);
	Acquire(-2);

	PrintOut("\nTest 7: Using Acquire with numeric string instead of int......\n", 64);
	Acquire("2");

	PrintOut("\nTest 8: Using Acquire with character string instead of int....\n", 64);
	Acquire("hat");

	PrintOut("\nTest 9: Using Acquire with single character instead of int....\n", 64);
	while(lockIndex < 10)
	{
		lockIndex = CreateLock(1); /*Make enough lock to get a lock at index 9*/
	}
	Acquire('\t'); /* horizontal tab is ASCII 9. 
				   Should not Acquire lock 9, instead should reject since non-int.
				   But could not find a way to prevent this use of characters that 
				   have ascii values within acceptable range of locks*/

	PrintOut("\nTest 10: Using Acquire with null string instead of int........\n", 64);
	Acquire("");
}

void ReleaseTest()
{
    int lockIndex;

    PrintOut("\n\n**********************RELEASE TESTS**********************\n\n", 61); 

    /*Correct Use - Should Pass*/
    PrintOut("Test 1: Using Release after a Acquire on same lock.............\n", 64);
    lockIndex = CreateLock(); /*Create a lock. An int that indexes the locks location in an array in the kernel will be returned*/
    Acquire(lockIndex); /*Acquire a lock. The int indexes the lock's location in an array in the kernel. Returns error code, 0 - SUCCESS*/
    Release(lockIndex); /*Release a lock. The int indexes the lock's location in an array in the kernel. Returns error code, 0 - SUCCESS*/
    
	PrintOut("\nTest 2: Using Release on newly created lock...................\n", 64);
    lockIndex = CreateLock();

    /*Incorrect Use - Should Fail and print error message*/
    PrintOut("\nTest 3: Using Release on uninitialized lock...................\n", 64);
    Release(150);

    PrintOut("\nTest 4: Using Release after a Release on same lock............\n", 64);
	lockIndex = CreateLock();
    Acquire(lockIndex);
    Release(lockIndex);
    Release(lockIndex);
    
    /*Incorrect Use - Should Fail and print debug message from Kernel code. Must run in debug to see (-d) e.g. nachos -x ../test/lockcv -d*/    
    PrintOut("\nTest 5: Using Release on a lock index out of bounds...........\n", 64);
	Release(500);
	
    PrintOut("\nTest 6: Using Release on a negative lock index................\n", 64);
    Release(-2);

	PrintOut("\nTest 7: Using Release with numeric string instead of int......\n", 64);
	Release("2");

	PrintOut("\nTest 8: Using Release with character string instead of int....\n", 64);
	Release("hat");

	PrintOut("\nTest 9: Using Release with single character instead of int....\n", 64);
	while(lockIndex < 10)
	{
		lockIndex = CreateLock(1); /*Make enough lock to get a lock at index 9*/
	}
	Release('\t'); /* horizontal tab is ASCII 9. 
				   Should not Release lock 9, instead should reject since non-int.
				   But could not find a way to prevent this use of characters that 
				   have ascii values within acceptable range of locks*/

	PrintOut("\nTest 10: Using Release with null string instead of int........\n", 64);
	Release("");
}

void DestroyLockTest()
{
    int lockIndex, i;
    	
    PrintOut("\n\n********************DESTROYLOCK TESTS********************\n\n", 61); 
 
    /*Correct Use - Should Pass*/

    PrintOut("Test 1: Using Destroy after a create...........................\n", 64);	
    lockIndex = CreateLock();
    DestroyLock(lockIndex);

	
    PrintOut("\nTest 2: Using Destroy after an Acquire........................\n", 64);
    lockIndex = CreateLock();
    Acquire(lockIndex);
    DestroyLock(lockIndex);

	PrintOut("\nTest 3: Using Destroy after a Release without Acquire.........\n", 64, ConsoleOutput); 
    lockIndex = CreateLock();
    Release(lockIndex);
    DestroyLock(lockIndex);

	PrintOut("\nTest 4: Using Create/Acquire/Release/Destroy..................\n", 64, ConsoleOutput); 
    lockIndex = CreateLock(); /*Create a lock. An int that indexes the locks location in an array in the kernel will be returned*/
    Acquire(lockIndex); /*Acquire a lock. The int indexes the lock's location in an array in the kernel. Returns error code, 0 - SUCCESS*/
    Release(lockIndex); /*Release a lock. The int indexes the lock's location in an array in the kernel. Returns error code, 0 - SUCCESS*/
	DestroyLock(lockIndex);
    
    /*Incorrect Use - Should Fail and print error message*/
	
    PrintOut("\nTest 5: Using Destroy on uninitialized lock...................\n", 64); 
    DestroyLock(50);
    
    PrintOut("\nTest 6: Using Destroy on a destroyed lock.....................\n", 64); 
	lockIndex = CreateLock();
    DestroyLock(lockIndex);
    DestroyLock(lockIndex);
    
	PrintOut("\nTest 7: Using Destroy multiple times in a row on same lock....\n", 64);
	lockIndex = CreateLock();
    for(i=0; i<5; i++)
    {
        DestroyLock(lockIndex);
        DestroyLock(lockIndex);
    }
  
    PrintOut("\nTest 8: Using Destroy at a lock index out of bounds...........\n", 64, ConsoleOutput);
    DestroyLock(500);

	PrintOut("\nTest 9: Using Destroy at a negative lock index ...............\n", 64, ConsoleOutput);
    DestroyLock(-2);

	PrintOut("\nTest 10: Using Destroy with numeric string instead of int.....\n", 64);
	DestroyLock("2");

	PrintOut("\nTest 11: Using Destroy with character string instead of int...\n", 64);
	DestroyLock("hat");

	PrintOut("\nTest 12: Using Destroy with single character instead of int...\n", 64);
	while(lockIndex < 10)
	{
		lockIndex = CreateLock(1); /*Make enough lock to get a lock at index 9*/
	}
	DestroyLock('\t'); /* horizontal tab is ASCII 9. 
				   Should not Release lock 9, instead should reject since non-int.
				   But could not find a way to prevent this use of characters that 
				   have ascii values within acceptable range of locks*/

	PrintOut("\nTest 13: Using Destroy with null string instead of int........\n", 64);
	DestroyLock("");
}

void Fork_Test()
{
	PrintOut("\n\n********************TESTS WITH FORK*******************\n\n", 58);
	threadA = &ThreadA;
	threadB = &ThreadB;
	
	Fork(threadA);
	Fork(threadB);
}

void ThreadA()
{
	PrintOut("ThreadA!\n", 9);
	Acquire(threadALock, 1); /*Thread B will try to release this lock*/
	Acquire(FTLock, 1);
	PrintOut("1 ThreadA\n", 10);
	Wait(FTCV, FTLock, 1);
	PrintOut("3 ThreadA\n", 10);
	Signal(FTCV, FTLock, 1);
	Release(FTLock, 1);
	Exit(0);
}

void ThreadB()
{
	int lockIndex, i;;

	PrintOut("ThreadB!\n", 9);
	Acquire(FTLock, 1);
	PrintOut("2 ThreadB\n", 10);
	Signal(FTCV, FTLock, 1);
	Wait(FTCV, FTLock, 1);
	PrintOut("4 ThreadB\n", 10);
	Release(FTLock, 1);

	PrintOut("\nTest 1: Trying to Release a lock owned by another thread......\n", 64);
	Release(threadALock, 1);
	Exit(0);
}

